package com.example.gomoku;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
import java.util.Random;
import java.util.Stack;

public class GomokuView extends View {

    private static final int BOARD_SIZE = 13;
    private static final int INTERVAL = 80;
    private static final int OFFSET = 30;
    private static final int EMPTY = 0;
    private static final int BLACK = 1;
    private static final int WHITE = 2;
    private Paint boardPaint;
    private Paint blackPaint;
    private Paint whitePaint;

    private int[][] board;
    private boolean isBlackTurn;
    private Random random;

    private boolean isPaused;
    private Stack<int[]> moveStack;

    private OnWinListener onWinListener;
    private OnPiecePlacedListener onPiecePlacedListener;


    public GomokuView(Context context, @Nullable AttributeSet attrs) {
        super( context, attrs );
        init();
    }

    private void init() {
        boardPaint = new Paint();
        boardPaint.setColor( Color.BLACK );
        boardPaint.setStyle( Paint.Style.STROKE );
        boardPaint.setStrokeWidth( 10 );

        blackPaint = new Paint();
        blackPaint.setColor( Color.BLACK );
        blackPaint.setStyle( Paint.Style.FILL );

        whitePaint = new Paint();
        whitePaint.setColor( Color.WHITE );
        whitePaint.setStyle( Paint.Style.FILL );

        board = new int[BOARD_SIZE][BOARD_SIZE];
        isBlackTurn = true;
        random = new Random();
        isPaused = false;
        moveStack = new Stack<>();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw( canvas );
        drawBoard( canvas );
        drawPieces( canvas );
    }

    private void drawBoard(Canvas canvas) {
        int startX = OFFSET;
        int startY = OFFSET;
        int endX = startX + (INTERVAL * (BOARD_SIZE - 1));
        int endY = startY + (INTERVAL * (BOARD_SIZE - 1));
        for (int i = 0; i < BOARD_SIZE; i++) {
            canvas.drawLine( startX, startY + i * INTERVAL, endX, startY + i * INTERVAL, boardPaint );
            canvas.drawLine( startX + i * INTERVAL, startY, startX + i * INTERVAL, endY, boardPaint );
        }
    }

    private void drawPieces(Canvas canvas) {
        for (int i = 0; i < BOARD_SIZE; i++) {
            for (int j = 0; j < BOARD_SIZE; j++) {
                if (board[i][j] == 1) {
                    canvas.drawCircle( OFFSET + i * INTERVAL, OFFSET + j * INTERVAL, INTERVAL / 2 - 5, blackPaint );
                } else if (board[i][j] == 2) {
                    canvas.drawCircle( OFFSET + i * INTERVAL, OFFSET + j * INTERVAL, INTERVAL / 2 - 5, whitePaint );
                }
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (isPaused) {
            return true;
        }

        if (event.getAction() == MotionEvent.ACTION_UP) {
            int x = (int) ((event.getX() - OFFSET + INTERVAL / 2) / INTERVAL);
            int y = (int) ((event.getY() - OFFSET + INTERVAL / 2) / INTERVAL);

            if (x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE && board[x][y] == EMPTY) {
                placePiece( x, y, WHITE );
                if (checkWin( x, y )) {
                    if (onWinListener != null) {
                        onWinListener.onWin( true );
                    }
                    return true;
                }
                isBlackTurn = false;
                aiMove();
                invalidate();
            }
        }
        return true;
    }

    private void placePiece(int x, int y, int white) {
        board[x][y] = isBlackTurn ? 1 : 2;
        moveStack.push( new int[]{x, y} );
        if (onPiecePlacedListener != null) {
            onPiecePlacedListener.onPiecePlaced();
        }
    }

    private void aiMove() {
        int bestX = -1, bestY = -1;
        int maxScore = -1;
        for (int x = 0; x < BOARD_SIZE; x++) {
            for (int y = 0; y < BOARD_SIZE; y++) {
                if (board[x][y] == EMPTY) {
                    int score = evaluatePosition( x, y, WHITE );
                    if (score > maxScore) {
                        maxScore = score;
                        bestX = x;
                        bestY = y;
                    }
                }
            }
        }
        if (bestX != -1 && bestY != -1) {
            placePiece( bestX, bestY, WHITE );
            if (checkWin( bestX, bestY )) {
                if (onWinListener != null) {
                    onWinListener.onWin( false );
                }
            }
            isBlackTurn = true;
        }
    }

    private int evaluateMove(int x, int y) {
        // Evaluate the move based on the current board state
        // This is a simple heuristic, you can improve it by using more advanced algorithms
        int score = 0;
        int count = 0;

        // Check horizontal
        for (int i = 0; i < BOARD_SIZE; i++) {
            if (board[x][i] == 2) {
                count++;
            } else {
                break;
            }
        }
        score += count * count;

        count = 0;
        for (int i = BOARD_SIZE - 1; i >= 0; i--) {
            if (board[x][i] == 2) {
                count++;
            } else {
                break;
            }
        }
        score += count * count;

        // Check vertical
        count = 0;
        for (int i = 0; i < BOARD_SIZE; i++) {
            if (board[i][y] == 2) {
                count++;
            } else {
                break;
            }
        }
        score += count * count;

        count = 0;
        for (int i = BOARD_SIZE - 1; i >= 0; i--) {
            if (board[i][y] == 2) {
                count++;
            } else {
                break;
            }
        }
        score += count * count;

        // Check diagonal
        count = 0;
        for (int i = 0; i < BOARD_SIZE; i++) {
            if (board[x + i][y + i] == 2) {
                count++;
            } else {
                break;
            }
        }
        score += count * count;

        count = 0;
        for (int i = BOARD_SIZE - 1; i >= 0; i--) {
            if (board[x + i][y - i] == 2) {
                count++;
            } else {
                break;
            }
        }
        score += count * count;

        return score;
    }


    private boolean checkWin(int x, int y) {
        int player = board[x][y];
        return checkDirection( x, y, 1, 0, player ) || checkDirection( x, y, 0, 1, player )
                || checkDirection( x, y, 1, 1, player ) || checkDirection( x, y, 1, -1, player );
    }

    private boolean checkDirection(int x, int y, int dx, int dy, int player) {
        int count = 1;
        for (int i = 1; i < 5; i++) {
            int nx = x + i * dx, ny = y + i * dy;
            if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE && board[nx][ny] == player) {
                count++;
            } else {
                break;
            }
        }
        for (int i = 1; i < 5; i++) {
            int nx = x - i * dx, ny = y - i * dy;
            if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE && board[nx][ny] == player) {
                count++;
            } else {
                break;
            }
        }
        return count >= 5;
    }

    public void resetGame() {
        for (int i = 0; i < BOARD_SIZE; i++) {
            for (int j = 0; j < BOARD_SIZE; j++) {
                board[i][j] = 0;
            }
        }
        isBlackTurn = true;
        moveStack.clear();
        invalidate();
    }

    public void undoMove() {
        if (!moveStack.isEmpty()) {
            int[] lastMove = moveStack.pop();
            board[lastMove[0]][lastMove[1]] = 0;
            isBlackTurn = !isBlackTurn;
            invalidate();
        }
    }

    public void pauseGame() {
        isPaused = !isPaused;
    }

    public boolean isPaused() {
        return isPaused;
    }

    public int getBlackCount() {
        int count = 0;
        for (int i = 0; i < BOARD_SIZE; i++) {
            for (int j = 0; j < BOARD_SIZE; j++) {
                if (board[i][j] == 1) {
                    count++;
                }
            }
        }
        return count;
    }

    public int getWhiteCount() {
        int count = 0;
        for (int i = 0; i < BOARD_SIZE; i++) {
            for (int j = 0; j < BOARD_SIZE; j++) {
                if (board[i][j] == 2) {
                    count++;
                }
            }
        }
        return count;
    }

    public void setOnWinListener(OnWinListener listener) {
        this.onWinListener = listener;
    }

    public void setOnPiecePlacedListener(OnPiecePlacedListener listener) {
        this.onPiecePlacedListener = listener;
    }

    public interface OnWinListener {
        void onWin(boolean isBlack);
    }

    public interface OnPiecePlacedListener {
        void onPiecePlaced();
    }

    private int evaluatePosition(int x, int y, int player) {
        int score = 0;
        score += evaluateDirection( x, y, 1, 0, player ); // 横向
        score += evaluateDirection( x, y, 0, 1, player ); // 纵向
        score += evaluateDirection( x, y, 1, 1, player ); // 主对角线
        score += evaluateDirection( x, y, 1, -1, player ); // 副对角线
        return score;
    }

    private int evaluateDirection(int x, int y, int dx, int dy, int player) {
        int score = 0;
        int count = 0;
        int opponent = (player == BLACK) ? WHITE : BLACK;
        for (int i = -4; i <= 4; i++) {
            int nx = x + i * dx, ny = y + i * dy;
            if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE) {
                if (board[nx][ny] == player) {
                    count++;
                } else if (board[nx][ny] == opponent) {
                    count = 0;
                    break;
                }
            }
        }
        if (count == 4) {
            score += 10000;
        } else if (count == 3) {
            score += 1000;
        } else if (count == 2) {
            score += 100;
        } else if (count == 1) {
            score += 10;
        }
        return score;
    }
}